//
//  PPNetEngine.m
//  PPNetworking
//
//  Created by 邓标祥 on 2020/12/15.
//

#import "PPNetEngine.h"
#import "AFNetworking.h"

#warning 备用自定义线程队列
static dispatch_queue_t pp_request_completion_callback_queue() {
    static dispatch_queue_t _pp_request_completion_callback_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _pp_request_completion_callback_queue = dispatch_queue_create("com.ppnetworking.request.completion.callback.queue", DISPATCH_QUEUE_CONCURRENT);
    });
    return _pp_request_completion_callback_queue;
}

static NSString *const xRequestMark = @"+";

@interface PPNetEngine(){
    dispatch_semaphore_t _lock;
}

@property (nonatomic, strong) AFURLSessionManager *sessionManager;

@property (nonatomic, strong) AFHTTPRequestSerializer *afHTTPRequestSerializer;
@property (nonatomic, strong) AFJSONRequestSerializer *afJSONRequestSerializer;
@property (nonatomic, strong) AFPropertyListRequestSerializer *afPListRequestSerializer;

@property (nonatomic, strong) AFHTTPResponseSerializer *afHTTPResponseSerializer;
@property (nonatomic, strong) AFJSONResponseSerializer *afJSONResponseSerializer;
@property (nonatomic, strong) AFXMLParserResponseSerializer *afXMLResponseSerializer;
@property (nonatomic, strong) AFPropertyListResponseSerializer *afPListResponseSerializer;

@end

@implementation PPNetEngine

+ (instancetype)sharedEngine {
    static id sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[PPNetEngine alloc]init];
    });
    return sharedInstance;
}

- (instancetype)init{
    if (self = [super init]) {
        _lock = dispatch_semaphore_create(1);
    }
    return self;
}

- (void)sendRequest:(PPNetRequest *)request completionHandler:(PPNetCompletionHandler)completionHandler{
    switch (request.requestType) {
        case PPNetRequest_normal:
            /// Http 请求
            [self pp_dataTaskWithRequest:request completionHandler:completionHandler];
            break;
        default:
            [self pp_dataTaskWithRequest:request completionHandler:completionHandler];
            break;
    }
}

#pragma mark - 取消单个请求任务
- (void)cancelRequestByIdentifier:(NSString *)identifier {
    if (identifier.length == 0) return;
    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
    NSArray *tasks = nil;
    
    if ([identifier hasPrefix:xRequestMark]) {
        tasks = self.sessionManager.tasks;
        if (tasks.count > 0) {
            [tasks enumerateObjectsUsingBlock:^(NSURLSessionTask *task, NSUInteger idx, BOOL *stop) {
                NSString *taskIdentifier = [NSString stringWithFormat:@"%@%lu", xRequestMark, (unsigned long)task.taskIdentifier];
                if ([taskIdentifier isEqualToString:identifier]) {
                    [task cancel];
                    *stop = YES;
                }
            }];
        }
    }
    dispatch_semaphore_signal(_lock);
}

#pragma mark - 取消正在执行的全部请求任务
- (void)cancelAllOperations{
    if (self.sessionManager.tasks.count > 0) {
        [self.sessionManager.tasks makeObjectsPerformSelector:@selector(cancel)];
    }
//    [self.sessionManager.operationQueue cancelAllOperations];
}

#pragma mark -- 私有方法
/// 创建请求任务并发送请求
- (void)pp_dataTaskWithRequest:(PPNetRequest *)request
             completionHandler:(PPNetCompletionHandler)completionHandler{
    NSString *httpMethod = nil;
    static dispatch_once_t onceToken;
    static NSArray *httpMethodArray = nil;
    dispatch_once(&onceToken, ^{
        httpMethodArray = @[@"GET", @"POST", @"HEAD", @"DELETE", @"PUT", @"PATCH"];
    });
    if (request.httpMethod >= 0 && request.httpMethod < httpMethodArray.count) {
        httpMethod = httpMethodArray[request.httpMethod];
    }
    
    AFURLSessionManager *sessionManager = self.sessionManager;
    AFHTTPRequestSerializer *requestSerializer = [self pp_getRequestSerializer:request];
    
    NSError *serializationError = nil;
    NSMutableURLRequest *urlRequest = [requestSerializer requestWithMethod:httpMethod
                                                                 URLString:request.url
                                                                parameters:request.parameters
                                                                     error:&serializationError];
    if (serializationError) {
        if (completionHandler) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionHandler(nil, serializationError);
            });
        }
        return;
    }
    /// 设置请求头
    [self pp_processURLRequest:urlRequest byRequest:request];
    
    NSURLSessionDataTask *dataTask = nil;
    
    __weak typeof(self)weakSelf = self;
    //@PPWeakObj(self);
    dataTask = [sessionManager dataTaskWithRequest:urlRequest
                                    uploadProgress:nil
                                  downloadProgress:nil
                                 completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
//        @PPStrongObj(self);
        __strong typeof(weakSelf) strongSelf = weakSelf;
        /// 数据解析
        [strongSelf pp_processResponse:response object:responseObject error:error
               request:request completionHandler:completionHandler];
    }];
    
    [self pp_setIdentifierForReqeust:request
                      taskIdentifier:dataTask.taskIdentifier];
    /// 开启任务
    [dataTask resume];
}

#pragma mark - 设置header和请求时长
- (void)pp_processURLRequest:(NSMutableURLRequest *)urlRequest byRequest:(PPNetRequest *)request{
    if (request.headers.count > 0) {
        [request.headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
            [urlRequest setValue:value forHTTPHeaderField:field];
        }];
    }
    urlRequest.timeoutInterval = request.timeoutInterval;
}

#pragma mark - 拿到响应的二进制数据后，在去根据配置的响应序列化去解析数据
- (void)pp_processResponse:(NSURLResponse *)response
                    object:(id)responseObject
                     error:(NSError *)error
                   request:(PPNetRequest *)request
         completionHandler:(PPNetCompletionHandler)completionHandler {
    NSError *serializationError = nil;
    if (request.responseSerializerType != PPNetResponseSerializer_raw) {
        AFHTTPResponseSerializer *responseSerializer = [self pp_getResponseSerializer:request];
        responseObject = [responseSerializer responseObjectForResponse:response data:responseObject error:&serializationError];
    }
    
    if (completionHandler) {
        if (serializationError) {
            completionHandler(nil, serializationError);
        } else {
            completionHandler(responseObject, error);
        }
    }
}

#pragma mark - 拿到请求任务返回的唯一任务识别标识绑定在request对象上
- (void)pp_setIdentifierForReqeust:(PPNetRequest *)request
                    taskIdentifier:(NSUInteger)taskIdentifier {
    NSString *identifier = [NSString stringWithFormat:@"+%lu", (unsigned long)taskIdentifier];
    [request setValue:identifier forKey:@"_identifier"];
}

#pragma mark - 根据request配置的请求序列化类型拿到对应的requestSerializer
- (AFHTTPRequestSerializer *)pp_getRequestSerializer:(PPNetRequest *)request {
    switch (request.requestSerializerType) {
        case PPNetRequestSerializer_raw:
            return self.afHTTPRequestSerializer;
            break;
            
        case PPNetRequestSerializer_json:
            return self.afJSONRequestSerializer;
            break;
            
        case PPNetRequestSerializer_plist:
            return self.afPListRequestSerializer;
            break;
        default:
            return nil;
            break;
    }
}

#pragma mark - 根据request配置的响应序列化类型拿到对应的responseSerializer
- (AFHTTPResponseSerializer *)pp_getResponseSerializer:(PPNetRequest *)request {
    switch (request.responseSerializerType) {
        case PPNetResponseSerializer_raw:
            return self.afHTTPResponseSerializer;
            break;
            
        case PPNetResponseSerializer_json:
            return self.afJSONResponseSerializer;
            break;
            
        case PPNetResponseSerializer_plist:
            return self.afPListResponseSerializer;
            break;
            
        case PPNetResponseSerializer_xml:
            return self.afXMLResponseSerializer;
            break;
        default:
            return nil;
            break;
    }
}

- (AFURLSessionManager *)sessionManager {
    if (!_sessionManager) {
        _sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:nil];
        _sessionManager.responseSerializer = self.afHTTPResponseSerializer; //< 把一开始的响应序列化设置成父类
        _sessionManager.operationQueue.maxConcurrentOperationCount = 5;
        //                _sessionManager.completionQueue = pp_request_completion_callback_queue();
    }
    return _sessionManager;
}

- (AFHTTPRequestSerializer *)afHTTPRequestSerializer {
    if (!_afHTTPRequestSerializer) {
        _afHTTPRequestSerializer = [AFHTTPRequestSerializer serializer];
    }
    return _afHTTPRequestSerializer;
}

- (AFJSONRequestSerializer *)afJSONRequestSerializer {
    if (!_afJSONRequestSerializer) {
        _afJSONRequestSerializer = [AFJSONRequestSerializer serializer];
        [_afJSONRequestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    }
    return _afJSONRequestSerializer;
}

- (AFPropertyListRequestSerializer *)afPListRequestSerializer {
    if (!_afPListRequestSerializer) {
        _afPListRequestSerializer = [AFPropertyListRequestSerializer serializer];
    }
    return _afPListRequestSerializer;
}

- (AFHTTPResponseSerializer *)afHTTPResponseSerializer {
    if (!_afHTTPResponseSerializer) {
        _afHTTPResponseSerializer = [AFHTTPResponseSerializer serializer];
    }
    return _afHTTPResponseSerializer;
}

- (AFJSONResponseSerializer *)afJSONResponseSerializer {
    if (!_afJSONResponseSerializer) {
        _afJSONResponseSerializer = [AFJSONResponseSerializer serializer];
        _afJSONResponseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html", @"application/json", @"text/json",  @"text/javascript", @"text/plain", nil];
    }
    return _afJSONResponseSerializer;
}

- (AFXMLParserResponseSerializer *)afXMLResponseSerializer {
    if (!_afXMLResponseSerializer) {
        _afXMLResponseSerializer = [AFXMLParserResponseSerializer serializer];
    }
    return _afXMLResponseSerializer;
}

- (AFPropertyListResponseSerializer *)afPListResponseSerializer {
    if (!_afPListResponseSerializer) {
        _afPListResponseSerializer = [AFPropertyListResponseSerializer serializer];
    }
    return _afPListResponseSerializer;
}

@end
